package com.xbongbong.pro.service.dingtalk.impl;

import com.alibaba.dingtalk.openapi.enums.InstanceResultEnum;
import com.alibaba.dingtalk.openapi.helper.AuthHelper;
import com.alibaba.dingtalk.openapi.helper.DingtalkOaHelper;
import com.alibaba.dingtalk.openapi.helper.Env;
import com.alibaba.dingtalk.openapi.vo.oa.InstanceAddDTO;
import com.alibaba.dingtalk.openapi.vo.oa.InstanceFormDTO;
import com.alibaba.dingtalk.openapi.vo.oa.InstanceResultDTO;
import com.alibaba.dingtalk.openapi.vo.oa.InstanceUpdateDTO;
import com.alibaba.dingtalk.openapi.vo.oa.TaskAddDTO;
import com.alibaba.dingtalk.openapi.vo.oa.TaskCancelDTO;
import com.alibaba.dingtalk.openapi.vo.oa.TaskDetailDTO;
import com.alibaba.dingtalk.openapi.vo.oa.TaskFormDTO;
import com.alibaba.dingtalk.openapi.vo.oa.TaskResultDTO;
import com.alibaba.dingtalk.openapi.vo.oa.TaskUpdateDTO;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.pro.companyconfig.pojo.CompanyConfigWorkrecordValuePojo;
import com.xbongbong.pro.config.ProBaseConfig;
import com.xbongbong.pro.enums.workrecord.enums.WorkrecordResultEnum;
import com.xbongbong.pro.enums.workrecord.enums.WorkrecordStatusEnum;
import com.xbongbong.pro.enums.workrecord.enums.WorkrecordTemplateEnum;
import com.xbongbong.pro.message.pojo.PushRefTypePojo;
import com.xbongbong.pro.process.pojo.dto.WorkrecordInstanceDTO;
import com.xbongbong.pro.process.pojo.dto.WorkrecordTaskDTO;
import com.xbongbong.pro.process.pojo.dto.WorkrecordTaskOption;
import com.xbongbong.pro.process.pojo.dto.WorkrecordUpdateTaskDTO;
import com.xbongbong.pro.service.dingtalk.WorkflowDingtalkService;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.saas.enums.DingtalkPageUrlEnum;
import com.xbongbong.sys.domain.entity.CompanyConfigEntity;
import com.xbongbong.sys.model.CompanyConfigModel;
import com.xbongbong.workflow.domain.entity.WorkflowTaskEntity;
import com.xbongbong.workflow.domain.entity.WorkflowTaskNodeEntity;
import com.xbongbong.workflow.model.WorkflowTaskModel;
import com.xbongbong.workflow.model.WorkflowTaskNodeModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 钉钉OA待办服务实现类
 *
 * @author LiZhikang
 * @date 2023/3/14 16:40
 */
@Service
@Slf4j
public class WorkflowDingtalkServiceImpl implements WorkflowDingtalkService {

    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private AuthHelper authHelper;
    @Resource
    private ProBaseConfig proBaseConfig;
    @Resource
    private WorkflowTaskModel workflowTaskModel;
    @Resource
    private WorkflowTaskNodeModel workflowTaskNodeModel;
    @Resource
    DingtalkOaHelper dingtalkOaHelper;

    @Override
    public String getDingtalkProcessCode(String corpid) {
        CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.DINGTALK_WORKRECORD.getAlias(), corpid);
        if (companyConfigEntity == null) {
            // 配置表没有记录,表示没开启
            return null;
        }
        CompanyConfigWorkrecordValuePojo configValuePojo = JSON.parseObject(companyConfigEntity.getConfigValue(), CompanyConfigWorkrecordValuePojo.class);
        //开启则返回对应值
        return Objects.equals(configValuePojo.getEnable(), BasicConstant.ONE) ? configValuePojo.getDingtalkProcessCode() : null;
    }

    @Override
    public void createDingtalkInstance(WorkrecordInstanceDTO workrecordInstanceDTO) {
        String corpid = workrecordInstanceDTO.getCorpid();
        String accessToken = getAccessToken(corpid);

        try {

            List<InstanceFormDTO> formList = new ArrayList<>();
            formList.add(new InstanceFormDTO(WorkrecordTemplateEnum.SUMMARY.getName(), workrecordInstanceDTO.getSummaryTitle(), "TextField"));
            formList.add(new InstanceFormDTO(WorkrecordTemplateEnum.USER_NAME.getName(), workrecordInstanceDTO.getUserName(), "TextField"));
            formList.add(new InstanceFormDTO(WorkrecordTemplateEnum.SUBMITTIME.getName(), DateTimeUtil.getStringEpochSecond(workrecordInstanceDTO.getSubmitTime()), "TextField"));

            InstanceAddDTO instanceAddDTO = new InstanceAddDTO();
            instanceAddDTO
                    .setTitle(workrecordInstanceDTO.getFormName())
                    .setOriginatorUserId(workrecordInstanceDTO.getUserId())
                    .setProcessCode(workrecordInstanceDTO.getDingtalkProcessCode())
                    .setUrl(formatLinkUrl(corpid, workrecordInstanceDTO.getOptions()))
                    .setFormComponentValueList(formList);

            InstanceResultDTO result = dingtalkOaHelper.createDingtalkInstance(instanceAddDTO, accessToken);

            if (Objects.nonNull(result) && Objects.nonNull(result.getProcessInstanceId())) {
                String processInstanceId = result.getProcessInstanceId();
                // 更新表
                WorkflowTaskEntity workflowTaskEntity = new WorkflowTaskEntity();
                workflowTaskEntity.setId(workrecordInstanceDTO.getOptions().getProcessTaskId());
                workflowTaskEntity.setCorpid(corpid);
                workflowTaskEntity.setDingtalkProcessInstanceId(processInstanceId);
                workflowTaskModel.update(workflowTaskEntity);
            }
        } catch (Exception e) {
            log.error("WorkflowDingtalkServiceImpl.createDingtalkInstance fail", e);
        }
    }

    @Override
    public void updateDingtalkInstance(String corpid, String processInstanceId, WorkrecordResultEnum workrecordResultEnum) {
        String accessToken = getAccessToken(corpid);

        try {
            InstanceUpdateDTO instanceUpdateDTO = null;
            switch (workrecordResultEnum) {
                case AGREE:
                    instanceUpdateDTO = new InstanceUpdateDTO(processInstanceId, InstanceResultEnum.AGREE.getStatus(), InstanceResultEnum.AGREE.getResult());
                    break;
                case REFUSE:
                    instanceUpdateDTO = new InstanceUpdateDTO(processInstanceId, InstanceResultEnum.REFUSE.getStatus(), InstanceResultEnum.REFUSE.getResult());
                    break;
                case TERMINATED:
                    instanceUpdateDTO = new InstanceUpdateDTO(processInstanceId, InstanceResultEnum.CANCEL.getStatus(), InstanceResultEnum.CANCEL.getResult());
                    break;
                default:
                    break;
            }
            dingtalkOaHelper.updateDingtalkInstance(instanceUpdateDTO, accessToken);
        } catch (Exception e) {
            log.error("WorkflowDingtalkServiceImpl.updateDingtalkInstance fail", e);
        }
    }

    @Override
    public void batchUpdateDingtalkInstance(String corpid, List<String> processInstanceIds, WorkrecordResultEnum workrecordResultEnum) {
        //钉钉接口可实现,目前系统中还没有相关功能
        return;
    }

    @Override
    public void createDingtalkTask(WorkrecordTaskDTO workrecordTaskDTO) {
        String corpid = workrecordTaskDTO.getCorpid();
        String accessToken = getAccessToken(corpid);
        String processInstanceId = workrecordTaskDTO.getDingtalkProcessInstanceId();

        try {
            //Activity前缀统一为"TASK-"+第一个nodeTaskId
            List<WorkrecordTaskOption> taskOptions = workrecordTaskDTO.getTaskOptions();
            String activityId = "TASK-" + taskOptions.get(0).getOptions().getProcessNodeTaskId();
            //同一个审批节点所有人url一样
            String url = formatLinkUrl(corpid, taskOptions.get(0).getOptions());
            //userId->processNodeTaskId映射
            Map<String, Object> userTaskMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

            //一次只能创建200个任务,200个任务为一组,同一个Activity
            List<List<WorkrecordTaskOption>> optionParts = Lists.partition(taskOptions, BasicConstant.TWO_HUNDRED);
            AtomicInteger groupId = new AtomicInteger(BasicConstant.ONE);
            optionParts.forEach(part -> {
                List<TaskDetailDTO> subTasks = part.stream().map(option -> {
                    String userId = option.getUserId();
                    PushRefTypePojo.Options options = option.getOptions();
                    userTaskMap.put(userId, options.getProcessNodeTaskId());
                    return new TaskDetailDTO(userId, url);
                }).collect(Collectors.toList());
                //activity拼子分组id
                String subActivityId = activityId + StringConstant.UNDER_LINE + groupId.getAndIncrement();
                TaskAddDTO taskAddDTO = new TaskAddDTO(processInstanceId, subActivityId, subTasks);

                //发送请求
                List<TaskResultDTO> taskList = dingtalkOaHelper.createDingtalkTask(taskAddDTO, accessToken);

                if (CollectionUtils.isNotEmpty(taskList)) {
                    List<WorkflowTaskNodeEntity> nodeTaskList = taskList.stream().map(task -> {
                        WorkflowTaskNodeEntity nodeTaskEntity = new WorkflowTaskNodeEntity();
                        //拿到processNodeTaskId
                        String nodeTaskId = userTaskMap.get(task.getUserId()).toString();
                        nodeTaskEntity.setId(Long.valueOf(nodeTaskId));
                        //addSignature有默认值,手动置空
                        nodeTaskEntity.setAddSignature(null);
                        //同一个分组下的activityId都一样
                        nodeTaskEntity.setDingtalkWorkrecordActivityId(subActivityId);
                        nodeTaskEntity.setDingtalkWorkrecordTaskId(String.valueOf(task.getTaskId()));
                        nodeTaskEntity.setDingtalkProcessInstanceId(processInstanceId);
                        return nodeTaskEntity;
                    }).collect(Collectors.toList());
                    // 批量更新(同一审批节点不可能出现大量分组,数据库操作就放在循环内了)
                    workflowTaskNodeModel.updateBatch(nodeTaskList, corpid);
                }
            });
        } catch (Exception e) {
            log.error("WorkflowDingtalkServiceImpl.createDingtalkTask fail", e);
        }
    }

    @Override
    public void updateDingtalkTask(WorkrecordUpdateTaskDTO workrecordUpdateTaskDTO, WorkrecordResultEnum workrecordResultEnum) {
        String accessToken = getAccessToken(workrecordUpdateTaskDTO.getCorpid());
        String taskId = workrecordUpdateTaskDTO.getDingtalkTaskId();
        String instanceId = workrecordUpdateTaskDTO.getDingtalkInstanceId();
        if (StringUtils.isEmpty(taskId)) {
            return;
        }

        try {
            TaskFormDTO taskFormDTO = null;
            switch (workrecordResultEnum) {
                case AGREE:
                    taskFormDTO = new TaskFormDTO(Long.valueOf(taskId), WorkrecordStatusEnum.COMPLETED.getAlias(), WorkrecordResultEnum.AGREE.getAlias());
                    break;
                case REFUSE:
                    taskFormDTO = new TaskFormDTO(Long.valueOf(taskId), WorkrecordStatusEnum.COMPLETED.getAlias(), WorkrecordResultEnum.REFUSE.getAlias());
                    break;
                case CANCELED:
                    taskFormDTO = new TaskFormDTO(Long.valueOf(taskId), WorkrecordStatusEnum.CANCELED.getAlias(), WorkrecordResultEnum.AGREE.getAlias());
                    break;
                default:
                    break;
            }
            TaskUpdateDTO taskUpdateDTO = new TaskUpdateDTO(instanceId, Collections.singletonList(taskFormDTO));
            //发送请求
            dingtalkOaHelper.updateDingtalkTask(taskUpdateDTO, accessToken);
        } catch (Exception e) {
            log.error("WorkflowDingtalkServiceImpl.updateDingtalkTask fail", e);
        }
    }

    @Override
    public void batchCancelDingtalkTask(WorkrecordUpdateTaskDTO workrecordUpdateTaskDTO) {
        String accessToken = getAccessToken(workrecordUpdateTaskDTO.getCorpid());

        try {
            TaskCancelDTO taskCancelDTO = new TaskCancelDTO();
            taskCancelDTO.setProcessInstanceId(workrecordUpdateTaskDTO.getDingtalkInstanceId());
            taskCancelDTO.setActivityId(workrecordUpdateTaskDTO.getDingtalkActivityId());
            //发送请求
            dingtalkOaHelper.cancelDingtalkTask(taskCancelDTO, accessToken);
        } catch (Exception e) {
            log.error("WorkflowDingtalkServiceImpl.batchCancelDingtalkTask fail", e);
        }
    }

    /**
     * 获取AccessToken
     *
     * @param corpid
     * @return
     */
    private String getAccessToken(String corpid) {
        try {
            return authHelper.getAccessToken(corpid);
        } catch (Exception e) {
            log.error("WorkflowDingtalkServiceImpl.getAccessToken fail", e);
        }
        return null;
    }

    /**
     * 格式化落地页链接
     *
     * @param corpid
     * @param options
     * @return
     */
    private String formatLinkUrl(String corpid, PushRefTypePojo.Options options) {
        return proBaseConfig.getPushFrontUrl() + "index.html?corpid=" + corpid + "&appid=" + Env.DING_XBB_APP_ID + "&dd_nav_bgcolor=ffff943e#"
                + String.format(DingtalkPageUrlEnum.WORKFLOW_DETAIL.getUrl(), options.getProcessTaskId(), options.getProcessNodeTaskId(),
                options.getAppId(), options.getMenuId(), options.getFormId(), options.getSaasMark(), options.getBusinessType(), options.getProcessType())
                + "&fromDingtalk=true";
    }
}
